﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Input;
using Plugins.Core;
using StockAccessors;
using StockAnalyst2.Chart;
using StockAnalyst2.Plugins.Visualization.Realtime;
using StockAnalyst2.StockGrid;
using StockAnalyst2.Plugins.Visualization.Historical;
using StockEngine.Historical;
using StockEngine.RealTime;
using Utils;
using Utils.Extensions;
using Utils.Wpf;

namespace StockAnalyst2
{
	internal class ApplicationCommandsVM : ViewModelBase
	{
		#region Attributes

		private readonly AcquisitionSystem _acquisitonSystem;
		private readonly ApplicationCloser _applicationCloser;
		private readonly Options.OptionsManager _appOptionsManager;
		private readonly StockGrid.OptionsManager _gridOptionsManager;
		private readonly HistoricalSystem _historicalSystem;
		private readonly IStockDataAccessor _accessor;

		private readonly MainToolBarVM _mainToolBarVM;
		private readonly GridVM _realTimeGridVM;
		private readonly GridVM _historicalGridVM;
		private readonly RealtimePluginViewerVM _realtimePluginViewerVM;
		private readonly HistoricalPluginViewerVM _historicalPluginViewerVM;
		private readonly ChartViewerVM _chartViewerVM;
		private readonly MainWindowVM _mainWindowVM;

		private readonly BackgroundWorker _startWorker;
		private readonly BackgroundWorker _historicalLoadWorker;

		private DateTime? _lastReadDay;
		private string _lastRealTimeNameFilter;
		private string _lastHistoricalNameFilter;

		#endregion

		#region Properties

		public ICommand ExitCommand { get; private set; }
		public ICommand ChangeCaptureRealTimeStatusCommand { get; private set; }
		public ICommand ViewRealTimeDataCommand { get; private set; }
		public ICommand ViewHistoricalDataCommand { get; private set; }
		public ICommand ViewRealtimePluginsCommand { get; private set; }
		public ICommand ViewHistoricalPluginsCommand { get; private set; }
		public ICommand GridOptionsCommand { get; private set; }
		public ICommand OptionsCommand { get; private set; }
		public ICommand OpenBrowserCommand { get; private set; }
		public ICommand MoveNextDayCommand { get; private set; }
		public ICommand MovePreviousDayCommand { get; private set; }
		public ICommand RefreshAvailableDaysCommand { get; private set; }
		public ICommand LoadDayCommand { get; private set; }
		public ICommand ClearDayCommand { get; private set; }
		public ICommand ApplyNameFilterCommand { get; private set; }
		public ICommand SelectPluginCommand { get; private set; }
		public ICommand ViewChartCommand { get; private set; }

		#endregion

		#region Events

		public event EventHandler<ErrorDectectedEventArgs> ErrorDetected;

		#endregion

		#region Public Methods

		public ApplicationCommandsVM(AcquisitionSystem acquisitonSystem, ApplicationCloser applicationCloser, Options.OptionsManager appOptionsManager, StockGrid.OptionsManager gridOptionsManager, MainToolBarVM mainToolBarVM, HistoricalSystem historicalSystem, IStockDataAccessor accessor, MainWindowVM mainWindowVM, ChartViewerVM chartViewerVM)
		{
			_acquisitonSystem = acquisitonSystem;
			_applicationCloser = applicationCloser;
			_appOptionsManager = appOptionsManager;
			_gridOptionsManager = gridOptionsManager;
			_mainToolBarVM = mainToolBarVM;
			_historicalSystem = historicalSystem;
			_accessor = accessor;

			_realTimeGridVM = mainWindowVM.RealTimeGridVM;
			_historicalGridVM = mainWindowVM.HistoricalGridVM;
			_realtimePluginViewerVM = mainWindowVM.RealtimePluginViewerVM;
			_historicalPluginViewerVM = mainWindowVM.HistoricalPluginViewerVM;
			_mainWindowVM = mainWindowVM;
			_chartViewerVM = chartViewerVM;

			_startWorker = new BackgroundWorker();
			_startWorker.DoWork += StartWorker_DoWork; // Local object
			_startWorker.RunWorkerCompleted += StartWorker_RunWorkerCompleted; // Local object

			_historicalLoadWorker = new BackgroundWorker();
			_historicalLoadWorker.DoWork += HistoricalLoadWorker_DoWork; // Local object
			_historicalLoadWorker.RunWorkerCompleted += HistoricalLoadWorker_RunWorkerCompleted; // Local object

			this.ExitCommand = new DelegateCommand(ExecuteExit, CanExit);
			this.ChangeCaptureRealTimeStatusCommand = new DelegateCommand(ExecuteChangeCaptureRealTimeStatus, CanChangeCaptureRealTimeStatus);
			this.ViewRealTimeDataCommand = new DelegateCommand(ExecuteViewRealTimeData, CanViewRealTimeData);
			this.ViewHistoricalDataCommand = new DelegateCommand(ExecuteViewHistoricalData, CanViewHistoricalData);
			this.ViewRealtimePluginsCommand = new DelegateCommand(ExecuteViewRealtimePlugins, CanViewRealtimePlugins);
			this.ViewHistoricalPluginsCommand = new DelegateCommand(ExecuteViewHistoricalPlugins, CanViewHistoricalPlugins);
			this.GridOptionsCommand = new DelegateCommand(ExecuteGridOptions);
			this.OptionsCommand = new DelegateCommand(ExecuteOptions);
			this.OpenBrowserCommand = new DelegateCommand(ExecuteOpenBrowser);
			this.MoveNextDayCommand = new DelegateCommand(ExecuteMoveNextDay, CanMoveNextDay);
			this.MovePreviousDayCommand = new DelegateCommand(ExecuteMovePreviousDay, CanMovePreviousDay);
			this.RefreshAvailableDaysCommand = new DelegateCommand(ExecuteRefreshAvailableDays, CanRefreshAvailableDays);
			this.LoadDayCommand = new DelegateCommand(ExecuteLoadDay, CanLoadDay);
			this.ClearDayCommand = new DelegateCommand(ExecuteClearDay, CanClearDay);
			this.ApplyNameFilterCommand = new DelegateCommand(ExecuteApplyNameFilter, CanApplyNameFilter);
			this.SelectPluginCommand = new DelegateCommand<IStrategyPlugin>(ExecuteSelectPlugin);
			this.ViewChartCommand = new DelegateCommand(ExecuteViewChart, CanViewChart);
		}

		protected virtual void OnErrorDetected(string message)
		{
			if (this.ErrorDetected != null)
			{
				this.ErrorDetected(this, new ErrorDectectedEventArgs(message));
			}
		}

		#endregion

		#region Event Handlers

		private void StartWorker_DoWork(object sender, DoWorkEventArgs e)
		{
			if (_acquisitonSystem.Information.State == AcquisitionSystemState.Enabled)
			{
				System.Threading.Thread.CurrentThread.Name = "Acquisition System Stopper";
				_acquisitonSystem.Stop();
			}
			else
			{
				System.Threading.Thread.CurrentThread.Name = "Acquisition System Starter";
				_acquisitonSystem.Start();
			}
		}

		private void StartWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			if (e.Error is InvalidConnectionDataException)
			{
				this.OnErrorDetected("The user or password entered at the options window is invalid.\n" +
								"Please, be aware that connecting with am invalid password will block your account after 3 retries\n" +
								"You must fix the connection settings and restart the application, it won't try to connect again until restarted.");

				_appOptionsManager.ShowOptionsWithMainWindow();
			}
			else if (e.Error != null)
			{
				this.OnErrorDetected("Unexpected error loading historical data.\nError message: " + e.Error.Message);
			}
		}

		private void HistoricalLoadWorker_DoWork(object sender, DoWorkEventArgs e)
		{
			if (_mainToolBarVM.SelectedDate != null)
			{
				System.Threading.Thread.CurrentThread.Name = "Historical System Loader";
				_historicalSystem.LoadDay(_mainToolBarVM.SelectedDate.Value);
			}
		}

		private void HistoricalLoadWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			if (e.Error != null)
			{
				this.OnErrorDetected("Unexpected error loading historical data.\nError message: " + e.Error.Message);
			}
		}

		#endregion

		#region Private Methods

		private void ExecuteExit()
		{
			try
			{
				_applicationCloser.Shutdown();
			}
			catch (Exception ex)
			{
				this.OnErrorDetected("Error exiting. " + ex.Message);
			}
		}

		private bool CanExit()
		{
			return !_applicationCloser.ShutdownDone;
		}

		private void ExecuteChangeCaptureRealTimeStatus()
		{
			_startWorker.RunWorkerAsync();
		}

		private bool CanChangeCaptureRealTimeStatus()
		{
			return _appOptionsManager.EffectiveSettings.RealTimeEnabled && _acquisitonSystem.Information.State != AcquisitionSystemState.Disabling && _acquisitonSystem.Information.State != AcquisitionSystemState.Enabling;
		}

		private void ExecuteViewRealTimeData()
		{
			try
			{
				_mainToolBarVM.IsRealTimeViewSelected = true;
				_mainToolBarVM.IsHistoricalViewSelected = false;
				_mainToolBarVM.IsRealtimePluginsViewSelected = false;
				_mainToolBarVM.IsHistoricalPluginsViewSelected = false;
				_mainToolBarVM.IsChartViewSelected = false;

				_realTimeGridVM.IsVisible = true;
				_historicalGridVM.IsVisible = false;
				_realtimePluginViewerVM.IsVisible = false;
				_historicalPluginViewerVM.IsVisible = false;
				_chartViewerVM.IsVisible = false;
			}
			catch (Exception ex)
			{
				this.OnErrorDetected("Error switching to realtime data. " + ex.Message);
			}
		}

		private bool CanViewRealTimeData()
		{
			return !_mainToolBarVM.IsRealTimeViewSelected;
		}

		private void ExecuteViewHistoricalData()
		{
			try
			{
				_mainToolBarVM.IsRealTimeViewSelected = false;
				_mainToolBarVM.IsHistoricalViewSelected = true;
				_mainToolBarVM.IsRealtimePluginsViewSelected = false;
				_mainToolBarVM.IsHistoricalPluginsViewSelected = false;
				_mainToolBarVM.IsChartViewSelected = false;

				_realTimeGridVM.IsVisible = false;
				_historicalGridVM.IsVisible = true;
				_realtimePluginViewerVM.IsVisible = false;
				_historicalPluginViewerVM.IsVisible = false;
				_chartViewerVM.IsVisible = false;
			}
			catch (Exception ex)
			{
				this.OnErrorDetected("Error switching to historical data. " + ex.Message);
			}
		}

		private bool CanViewHistoricalData()
		{
			return !_mainToolBarVM.IsHistoricalViewSelected;
		}

		private void ExecuteViewRealtimePlugins()
		{
			try
			{
				_mainToolBarVM.IsRealTimeViewSelected = false;
				_mainToolBarVM.IsHistoricalViewSelected = false;
				_mainToolBarVM.IsRealtimePluginsViewSelected = true;
				_mainToolBarVM.IsHistoricalPluginsViewSelected = false;
				_mainToolBarVM.IsChartViewSelected = false;

				_realTimeGridVM.IsVisible = false;
				_historicalGridVM.IsVisible = false;
				_realtimePluginViewerVM.IsVisible = true;
				_historicalPluginViewerVM.IsVisible = false;
				_chartViewerVM.IsVisible = false;
			}
			catch (Exception ex)
			{
				this.OnErrorDetected("Error switching to realtime plugins. " + ex.Message);
			}
		}

		private bool CanViewRealtimePlugins()
		{
			return !_mainToolBarVM.IsRealtimePluginsViewSelected && _mainWindowVM.PluginSystemVM.AreRealtimePluginsLoaded;
		}

		private void ExecuteViewHistoricalPlugins()
		{
			try
			{
				_mainToolBarVM.IsRealTimeViewSelected = false;
				_mainToolBarVM.IsHistoricalViewSelected = false;
				_mainToolBarVM.IsRealtimePluginsViewSelected = false;
				_mainToolBarVM.IsHistoricalPluginsViewSelected = true;
				_mainToolBarVM.IsChartViewSelected = false;

				_realTimeGridVM.IsVisible = false;
				_historicalGridVM.IsVisible = false;
				_realtimePluginViewerVM.IsVisible = false;
				_historicalPluginViewerVM.IsVisible = true;
				_chartViewerVM.IsVisible = false;
			}
			catch (Exception ex)
			{
				this.OnErrorDetected("Error switching to realtime plugins. " + ex.Message);
			}
		}

		private bool CanViewHistoricalPlugins()
		{
			return !_mainToolBarVM.IsHistoricalPluginsViewSelected && _mainWindowVM.PluginSystemVM.AreHistoricalPluginsLoaded;
		}

		private void ExecuteViewChart()
		{
			try
			{
				_mainToolBarVM.IsRealTimeViewSelected = false;
				_mainToolBarVM.IsHistoricalViewSelected = false;
				_mainToolBarVM.IsRealtimePluginsViewSelected = false;
				_mainToolBarVM.IsHistoricalPluginsViewSelected = false;
				_mainToolBarVM.IsChartViewSelected = true;

				_realTimeGridVM.IsVisible = false;
				_historicalGridVM.IsVisible = false;
				_realtimePluginViewerVM.IsVisible = false;
				_historicalPluginViewerVM.IsVisible = false;
				_chartViewerVM.IsVisible = true;
			}
			catch (Exception ex)
			{
				this.OnErrorDetected("Error switching to chart view. " + ex.Message);
			}
		}

		private bool CanViewChart()
		{
			return !_mainToolBarVM.IsChartViewSelected;
		}

		private void ExecuteGridOptions()
		{
			try
			{
				_gridOptionsManager.ShowOptions();
			}
			catch (Exception ex)
			{
				this.OnErrorDetected("Error showing grid options. " + ex.Message);
			}
		}

		private void ExecuteOptions()
		{
			try
			{
				_appOptionsManager.ShowOptionsWithMainWindow();
			}
			catch (Exception ex)
			{
				this.OnErrorDetected("Error showing general options. " + ex.Message);
			}
		}

		private void ExecuteOpenBrowser()
		{
			try
			{
				_accessor.OpenBrowser();
			}
			catch (Exception ex)
			{
				this.OnErrorDetected("Error showing browser. " + ex.Message);
			}

		}

		private void ExecuteRefreshAvailableDays()
		{
			try
			{
				List<DateTime> availableDays = _historicalSystem.GetAvailableDays();

				if (availableDays.Count > 0)
				{
					availableDays.Sort();

					_mainToolBarVM.FirstDate = availableDays[0];
					_mainToolBarVM.LastDate = availableDays[availableDays.Count - 1];

					_mainToolBarVM.DaysWithoutData.Clear();
					_mainToolBarVM.DaysWithoutData.AddRange(Date2.GetDatesNotInRange(_mainToolBarVM.FirstDate, _mainToolBarVM.LastDate, availableDays));
				}
			}
			catch (Exception ex)
			{
				this.OnErrorDetected("Error refreshing available days. " + ex.Message);
			}
		}

		private bool CanRefreshAvailableDays()
		{
			return !_historicalLoadWorker.IsBusy;
		}

		private void ExecuteMoveNextDay()
		{
			try
			{
				if (_mainToolBarVM.SelectedDate != null)
				{
					_mainToolBarVM.SelectedDate = _mainToolBarVM.SelectedDate.Value.GetNextNonWeekendDay();
					while (_mainToolBarVM.DaysWithoutData.Contains(_mainToolBarVM.SelectedDate.Value))
					{
						_mainToolBarVM.SelectedDate = _mainToolBarVM.SelectedDate.Value.GetNextNonWeekendDay();
					}
				}
			}
			catch (Exception ex)
			{
				this.OnErrorDetected("Error moving next day. " + ex.Message);
			}	
		}

		private bool CanMoveNextDay()
		{
			return _mainToolBarVM.SelectedDate != null && _mainToolBarVM.SelectedDate.Value < _mainToolBarVM.LastDate;
		}

		private void ExecuteMovePreviousDay()
		{
			try
			{
				if (_mainToolBarVM.SelectedDate != null)
				{
					_mainToolBarVM.SelectedDate = _mainToolBarVM.SelectedDate.Value.GetPreviousNonWeekendDay();
					while (_mainToolBarVM.DaysWithoutData.Contains(_mainToolBarVM.SelectedDate.Value))
					{
						_mainToolBarVM.SelectedDate = _mainToolBarVM.SelectedDate.Value.GetPreviousNonWeekendDay();
					}
				}
			}
			catch (Exception ex)
			{
				this.OnErrorDetected("Error moving previous day. " + ex.Message);
			}	
		}

		private bool CanMovePreviousDay()
		{
			return _mainToolBarVM.SelectedDate != null && _mainToolBarVM.SelectedDate.Value > _mainToolBarVM.FirstDate;
		}

		private void ExecuteLoadDay()
		{
			try
			{
				if (_mainToolBarVM.SelectedDate != null)
				{
					_lastReadDay = _mainToolBarVM.SelectedDate.Value;
					_historicalLoadWorker.RunWorkerAsync();
				}
			}
			catch (Exception ex)
			{
				this.OnErrorDetected("Error loading day. " + ex.Message);
			}	

		}

		private bool CanLoadDay()
		{
			return _mainToolBarVM.SelectedDate != null && !_historicalLoadWorker.IsBusy && _lastReadDay != _mainToolBarVM.SelectedDate.Value;
		}

		private void ExecuteClearDay()
		{
			try
			{
				_historicalSystem.Clear();
				_historicalSystem.InitializeSystem();
				_lastReadDay = null;
			}
			catch (Exception ex)
			{
				this.OnErrorDetected("Error cleaning day. " + ex.Message);
			}	
		}

		private bool CanClearDay()
		{
			return _historicalSystem.State == HistoricalSystemState.Loaded;
		}

		private void ExecuteApplyNameFilter()
		{
			try
			{
				if (_mainToolBarVM.IsRealTimeViewSelected)
				{
					_realTimeGridVM.NameFilter = _mainToolBarVM.RealTimeNameFilter;
					_lastRealTimeNameFilter = _mainToolBarVM.RealTimeNameFilter;
				}
				else
				{
					_historicalGridVM.NameFilter = _mainToolBarVM.HistoricalNameFilter;
					_lastHistoricalNameFilter = _mainToolBarVM.HistoricalNameFilter;
				}
			}
			catch (Exception ex)
			{
				this.OnErrorDetected("Error applying name filter. " + ex.Message);
			}	
		}

		private bool CanApplyNameFilter()
		{
			if (_mainToolBarVM.IsRealTimeViewSelected)
			{
				return _lastRealTimeNameFilter != _mainToolBarVM.RealTimeNameFilter;
			}
			else
			{
				return _lastHistoricalNameFilter != _mainToolBarVM.HistoricalNameFilter;
			}
		}

		private void ExecuteSelectPlugin(IStrategyPlugin plugin)
		{
			try
			{
				_historicalPluginViewerVM.Plugin = plugin;
			}
			catch (Exception ex)
			{
				this.OnErrorDetected("Error selecting plugin. " + ex.Message);
			}	
			
		}

		#endregion
	}
}
